#include <iostream>
#include <sstream>
#include <fstream>
#include "ConfigFileParser.h"

using namespace std;

namespace tsc
{
namespace common
{

//------------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------------
ConfigFileParser::ConfigFileParser(const string& aFileName)
  : mFileName(aFileName)
{
  parseFile();
} // ConfigFileParser::ConfigFileParser

//------------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------------
ConfigFileParser::~ConfigFileParser()
{

} // ConfigFileParser::~ConfigFileParser

//------------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------------
void ConfigFileParser::parseFile() throw(ios_base::failure)
{
  ifstream file;
  string   line;
  size_t   lineNumber = 0;
  string   errorMsg("ConfigFileParser::parseFile() ");

  file.open(mFileName.data());
  if (!file)
  {
    errorMsg.append("Error while opening configuration file " + mFileName + ".\n");
    ios_base::failure exc(errorMsg);
    throw exc;
  }

  while (getline(file, line))
  {
    ++lineNumber;
    string temp = line;

    // skip empty line
    if (temp.empty())
      continue;

    // skip comment line and/or if only whitespace
    if (temp.find(cCommentChar) != temp.npos)
    {
      temp.erase(temp.find(cCommentChar));
    }
    if (temp.find_first_not_of("\t ") == temp.npos)
      continue;

    // check syntax
    temp.erase(0, temp.find_first_not_of("\t "));
    if (temp[0] == cSeparatorChar)
    {
      errorMsg.append("Syntax error while parsing configuration file " +
                      mFileName + ":" + Converter::typeToString(lineNumber) +
                      " missing Key before Separator.\n");
      ios_base::failure exc(errorMsg);
      throw exc;
    }
    if (temp.find(cSeparatorChar) == temp.npos)
    {
      errorMsg.append("Syntax error while parsing configuration file " +
                      mFileName + ":" + Converter::typeToString(lineNumber) +
                      " missing Separator " + cSeparatorChar + ".\n");
      ios_base::failure exc(errorMsg);
      throw exc;
    }


    if (temp.find_first_not_of("\t ", temp.find(cSeparatorChar) + 1) == temp.npos)
    {
      errorMsg.append("ConfigFileParser::parseFile() Syntax error while parsing "
                      "configuration file " + mFileName + ":" + Converter::typeToString(lineNumber) +
                      " missing Value after Separator.\n");
      ios_base::failure exc(errorMsg);
      throw(exc);
    }

    parseLine(temp, lineNo);
  }

  file.close();
} // ConfigFileParser::parseFile

//------------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------------
void ConfigFileParser::parseLine(const std::string& aLine, size_t const aLineNumber) throw(std::ios_base::failure)
{
  string temp = aLine;
  string key;
  string value;
  string errorMsg("ConfigFileParser::parseLine() ");

  temp.erase(0, temp.find_first_not_of("\t "));
  size_t sepPos = temp.find('=');


  extractKey(key, sepPos, temp);
  extractValue(value, sepPos, temp);

  if (!isKeyPresent(key))
  {
    mKeyValMap.insert(std::pair<std::string, std::string>(key, value));
  }
  else
  {
    errorMsg.append("Error while parsing "
                    "configuration file " + mFileName + ":" + Converter::typeToString(lineNumber) +
                    " combination Key Value must be unique.\n");
    ios_base::failure exc(errorMsg);
    throw exc;
  }

} // ConfigFileParser::parseLine

//------------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------------
void ConfigFileParser::extractKey(
  string&       aKey,
  size_t const& aSeparatorPos,
  const string& aLine) const
{
  aKey = aLine.substr(0, aSeparatorPos);
  if (aKey.find('\t') != aLine.npos ||
      aKey.find(' ')  != aLine.npos)
    aKey.erase(aKey.find_first_of("\t "));
} // ConfigFileParser::extractKey

//------------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------------
void ConfigFileParser::extractValue(
  string&       aValue,
  size_t const& aSeparatorPos,
  const string& aLine) const
{
  aValue = aLine.substr(aSeparatorPos + 1);
  aValue.erase(0, aValue.find_first_not_of("\t "));
  aValue.erase(aValue.find_last_not_of("\t ") + 1);
} // ConfigFileParser::extractKey
